/*
 * Copyright (c) 2016. 速位中国
 */

package com.grpc.server.netty;
/*

                  写字楼里写字间，写字间里程序员；
                  程序人'猿'写程序，又拿程序换酒钱。
                  酒醒只在网上坐，酒醉还来网下眠；
                  酒醉酒醒日复日，网上网下年复年。
                  但愿老死电脑间，不愿鞠躬老板前；
                  奔驰宝马贵者趣，公交自行程序员。
                  别人笑我忒疯癫，我笑自己命太贱；
                  不见满街漂亮妹，哪个归得程序'猿'？
━━━━━━神兽出没━━━━━━
 ┏┓   ┏┓
┏┛┻━━━┛┻┓
┃       ┃
┃   ━   ┃
┃ ┳┛ ┗┳ ┃
┃       ┃
┃   ┻   ┃
┃       ┃
┗━┓   ┏━┛
  ┃   ┃                    神兽保佑, 永无BUG!
  ┃   ┃       Code is far away from bug with the animal protecting
  ┃   ┗━━━┓
  ┃       ┣┓
  ┃       ┏┛
  ┗┓┓┏━┳┓┏┛
   ┃┫┫ ┃┫┫
   ┗┻┛ ┗┻┛
 */

import com.grpc.server.config.PropertiesConfig;
import io.grpc.Server;
import io.grpc.netty.NettyServerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by 石头哥哥 on 1/12/16.
 * <p>
 * grpc
 * <p>
 * server.net
 * <p>
 * comments:  网络服务
 */
@Service
public class GrpcNetService {

    /**
     * logger
     */
    private final Logger logger = LoggerFactory.getLogger(GrpcNetService.class);

    /**
     * server
     */
    private Server server;

    @Resource
    private PropertiesConfig propertiesConfig;


    @Resource
    private NettyServerBuilder nettyServerBuilder;


    /**
     * handle logic  thread pool
     * 具体配置 同netty一致!!!
     */
    private static final ExecutorService executorService =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()
                    , new NamedThreadFactory("logic-handler"));

    /**
     * 启动服务
     *
     * @throws Exception
     */
    public void startGrpc() throws Exception {

        //init server builder,init login executor threads pool
        nettyServerBuilder.executor(executorService);

        String certChain = propertiesConfig.getCertChain();
        String privateKey = propertiesConfig.getPrivateKey();

        if (!Objects.equals(certChain, "") && !Objects.equals(privateKey, ""))
            nettyServerBuilder.useTransportSecurity
                    (new File(certChain), new File(privateKey));
        //bind service
        server = nettyServerBuilder.build().start();

        //register hook
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                // Use stderr here since the logger may has been reset by its JVM shutdown hook.
                logger.debug("*** shutting down gRPC server since JVM is shutting down");
                if (server != null) {
                    server.shutdown();// shut down
                }
                executorService.shutdown();
                logger.debug("*** server shut down");
            }
        });

        logger.info("grpc_server bind service port:{}", propertiesConfig.getGrpc_port());

    }

    /**
     * 主线程服务挂起
     * Await termination on the main thread since the grpc library uses daemon threads.
     *
     * @throws InterruptedException
     */
    public void awaitTermination() throws InterruptedException {
        server.awaitTermination();
    }


    /**
     * 线程池工厂
     */
    private static class NamedThreadFactory implements ThreadFactory {

        private int _prio;
        private String _name;
        private AtomicInteger _threadNumber = new AtomicInteger(1);
        private ThreadGroup _group;

        /**
         * @return
         */
        public String get_name() {
            return _name;
        }

        /**
         * @param name 线程池名
         */
        public NamedThreadFactory(String name) {
            _prio = Thread.NORM_PRIORITY;//线程默认优先级
            _name = name;
            _group = new ThreadGroup(_name);
        }


        /**
         * @param name 线程池名
         */
        public NamedThreadFactory(String name, int _prio) {
            _prio = _prio;
            _name = name;
            _group = new ThreadGroup(_name);
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(_group, r);
            t.setName(_name + "-" + "#-" + _threadNumber.getAndIncrement());
            t.setPriority(_prio);
            return t;
        }

        public ThreadGroup getGroup() {
            return _group;
        }
    }


}
